import time
import uuid

from http import HTTPStatus
from threading import Lock, Thread

import PSConfig

import logging
logger = logging.getLogger(__name__)

class SessionCritical():
    def __init__(self):
        self.__mutex = Lock()
        self.__sessions = {}
        self.__tasks = []

    def AddTask(self, task):
        self.__mutex.acquire()
        self.__tasks.append(task)
        self.__mutex.release()

    def GetSession(self, session):
        self.__mutex.acquire()
        result = self.__sessions[session] if session in self.__sessions else None
        self.__mutex.release()
        return result

    def CheckSessions(self):
        self.__mutex.acquire()
        # TODO
        # Need to decide how long to expire the session here
        self.__mutex.release()

    def CreateSession(self):
        session = str(uuid.uuid1())
        self.__mutex.acquire()
        self.__sessions[session] = {}
        self.__mutex.release()
        return session

    def CheckTasks(self):
        self.__mutex.acquire()
        current = time.time()
        for task in self.__tasks:
            if task.IsTimeout():
                # Note: currently do nothing on task timeout
                pass
        self.__mutex.release()

class SessionTask():
    def __init__(self, func, cmd, params):
        self.__startTime = None
        self.__func = func
        self.__task = Thread(target = self.__doRun, args = (cmd, params))
        self.__handled = False
        self.__status = HTTPStatus.INTERNAL_SERVER_ERROR
        self.__length = 0
        self.__result = None

    def Run(self):
        self.__startTime = time.time()
        self.__task.start()
        self.__task.join()
        return self.__handled, self.__status, self.__length, self.__result

    def IsTimeout(self):
        current = time.time()
        timeout = max(1, min(PSConfig.SESSION_TASK_TIMEOUT, 10))
        return False if self.__startTime is None else ((current - self.__startTime) > timeout)

    def __doRun(self, cmd, params):
        logger.debug(f"[SessionTask.doRun] Start, cmd = {cmd}, params = {params}")
        self.__handled, self.__status, self.__length, self.__result = self.__func(cmd, params)
        logger.debug(f"[SessionTask.doRun] Finish, handled = {self.__handled}, status = {self.__status}, length = {self.__length}, result = {self.__result}")

class SessionManager(object):
    _instance = None
    
    def __new__(cls, enableChecker = True):
        if cls._instance is None:
            logger.debug("[SessionManager.SessionManager]")
            cls._instance = super(SessionManager, cls).__new__(cls)
            cls._instance.__properties = {}
            cls._instance.__critical = SessionCritical()
            cls._instance.__enableChecker = True
            Thread(target = cls._instance.__doCheck).start()
        if not enableChecker:
            cls._instance.__enableChecker = False
        return cls._instance

    def SetProperty(self, name, value, session = None):
        self.__getProperties(session)[name] = value

    def GetProperty(self, name, session = None):
        return self.__getProperties(session)[name] if name in self.__getProperties(session) else None

    def HandleRequest(self, func, cmd, params):
        task = SessionTask(func, cmd, params)
        self.__critical.AddTask(task)
        return task.Run()

    def IsValidSession(self, session):
        return self.__critical.GetSession(session) is not None

    def CreateSession(self):
        return self.__critical.CreateSession()

    def __getProperties(self, session):
        if session is None or session == "":
            return self.__properties
        sessionProperties = self.__critical.GetSession(session)
        return self.__properties if sessionProperties is None else sessionProperties

    def __doCheck(self):
        interval = max(1, min(PSConfig.SESSION_CHECK_INTERVAL, 10))
        while self.__enableChecker:
            time.sleep(interval)
            self.__critical.CheckSessions()
            self.__critical.CheckTasks()
